﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Reflection;
using System.ServiceModel;
using System.Security.Policy;

namespace HomeFramework.Model
{
    public partial class Handler
    {
        public class Utility
        {
            internal const int DEFAULT_LIFETIME_IN_SECONDS = 120;

            [Serializable]
            private class handler
            {
                public byte[] AssemblyData { get; set; }
                public string AssemblyFullName { get; set; }
                public string ContractFullName { get; set; }
                public string HandlerFullName { get; set; }
                public bool Enabled { get; set; }
                public string Name { get; set; }
                public string Namespace { get; set; }
                public string Version { get; set; }
            }

            private class AssemblyReaderUtility : MarshalByRefObject, IDisposable
            {
                public IEnumerable<handler> ReadHandlersByAssembly(byte[] bytes)
                {
                    var ass = Assembly.Load(bytes);
                    var services = from c in ass.GetTypes()
                                   where c.IsInterface
                                   let serviceContract = (ServiceContractAttribute)c.GetCustomAttributes(typeof(ServiceContractAttribute), true).SingleOrDefault()
                                   where serviceContract != null
                                   let handler = (
                                                   from h in ass.GetTypes()
                                                   where h.IsClass && h.GetInterface(c.FullName) != null
                                                   select h
                                                   ).SingleOrDefault()
                                   where handler != null
                                   select new { ContractType = c, Handler = handler, ContractInfos = serviceContract };

                    if (services.Where(s=> !string.IsNullOrEmpty(s.ContractInfos.Namespace)).Count()>0)
                        throw new ArgumentException("Contract namespaces not allowed!");

                    return (
                        from service in services
                        select new handler()
                        {
                            AssemblyData = bytes,
                            AssemblyFullName = ass.FullName,
                            ContractFullName = service.ContractType.FullName,
                            HandlerFullName = service.Handler.FullName,
                            Enabled = false,
                            Name = !string.IsNullOrEmpty(service.ContractInfos.Name) ? service.ContractInfos.Name : service.ContractType.Name,
                            Namespace = !string.IsNullOrEmpty(service.ContractInfos.Namespace) ? service.ContractInfos.Namespace
                                : service.ContractType.Namespace,
                            Version = ass.GetName().Version.ToString(),
                        }
                        ).ToArray();
                }

                public string ReadAssemblyFullName(byte[] bytes)
                {
                    var a = Assembly.Load(bytes);
                    return a.FullName;
                }

                public void Dispose() { }
            }

            public static IEnumerable<Handler> ReadAssembly(byte[] bytes)
            {
                IEnumerable<Handler> risp;

                var domain = AppDomain.CreateDomain(Guid.NewGuid().ToString(), new Evidence(AppDomain.CurrentDomain.Evidence), AppDomain.CurrentDomain.SetupInformation);
                using (var utility = (AssemblyReaderUtility)domain.CreateInstanceAndUnwrap(typeof(AssemblyReaderUtility).Assembly.FullName, typeof(AssemblyReaderUtility).FullName))
                {
                    risp = from a in utility.ReadHandlersByAssembly(bytes)
                           select new Handler()
                           {
                               AssemblyData = a.AssemblyData,
                               AssemblyFullName = a.AssemblyFullName,
                               ContractFullName = a.ContractFullName,
                               Enabled = a.Enabled,
                               HandlerFullName = a.HandlerFullName,
                               Name = a.Name,
                               Namespace = a.Namespace,
                               Version = a.Version,
                           };
                }
                AppDomain.Unload(domain);

                return risp;
            }

            public static Reference ReadReference(byte[] bytes)
            {
                var risp = new Reference();
                risp.ReferenceAssembly = bytes;


                var domain = AppDomain.CreateDomain(Guid.NewGuid().ToString(), new Evidence(AppDomain.CurrentDomain.Evidence), AppDomain.CurrentDomain.SetupInformation);
                using (var utility = (AssemblyReaderUtility)domain.CreateInstanceAndUnwrap(typeof(AssemblyReaderUtility).Assembly.FullName, typeof(AssemblyReaderUtility).FullName))
                {
                    risp.ReferenceFullName = utility.ReadAssemblyFullName(bytes);
                }

                AppDomain.Unload(domain);

                return risp;
            }
        }

    }
}